<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="本文摘抄自 阮一峰 老师博客，仅供学习参考。原文地址：http:&#x2F;&#x2F;www.ruanyifeng.com&#x2F;blog&#x2F;2014&#x2F;10&#x2F;event-loop.html  一、为什么JavaScript是单线程？JavaScript 语言的一大特点就是单线程，也就是说，同一个时间只能做一件事。那么，为什么 JavaScript 不能有多个线程呢？这样能提高效率啊。 JavaScript 的单线程，与它">
<meta property="og:type" content="article">
<meta property="og:title" content="JavaScript 运行机制详解">
<meta property="og:url" content="http://yoursite.com/2017/06/12/JavaScript-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/index.html">
<meta property="og:site_name" content="Albert Guo">
<meta property="og:description" content="本文摘抄自 阮一峰 老师博客，仅供学习参考。原文地址：http:&#x2F;&#x2F;www.ruanyifeng.com&#x2F;blog&#x2F;2014&#x2F;10&#x2F;event-loop.html  一、为什么JavaScript是单线程？JavaScript 语言的一大特点就是单线程，也就是说，同一个时间只能做一件事。那么，为什么 JavaScript 不能有多个线程呢？这样能提高效率啊。 JavaScript 的单线程，与它">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE21c14910a081cf794567059dd18e247f/5696">
<meta property="og:image" content="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE9b30fd1cb0660cbab875c88b3bd2d287/5698">
<meta property="og:image" content="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE3a8233aa379e3bc6a52b742963e70b0f/5700">
<meta property="article:published_time" content="2017-06-12T12:58:20.000Z">
<meta property="article:modified_time" content="2020-07-20T13:05:32.352Z">
<meta property="article:author" content="Albert Guo">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE21c14910a081cf794567059dd18e247f/5696">

<link rel="canonical" href="http://yoursite.com/2017/06/12/JavaScript-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>JavaScript 运行机制详解 | Albert Guo</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Albert Guo</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-bars fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-address-card fa-fw"></i>简介</a>

  </li>
        <li class="menu-item menu-item-webpack">

    <a href="/webpack/" rel="section"><i class="fa fa-bookmark fa-fw"></i>webpack</a>

  </li>
        <li class="menu-item menu-item-javascript">

    <a href="/JavaScript/" rel="section"><i class="fa fa-bookmark fa-fw"></i>ECMAScript 5.1</a>

  </li>
        <li class="menu-item menu-item-ecmascript6">

    <a href="/ECMAScript6/" rel="section"><i class="fa fa-bookmark fa-fw"></i>ECMAScript 6</a>

  </li>
        <li class="menu-item menu-item-react">

    <a href="/React/" rel="section"><i class="fa fa-bookmark fa-fw"></i>React</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/06/12/JavaScript-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          JavaScript 运行机制详解
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-06-12 20:58:20" itemprop="dateCreated datePublished" datetime="2017-06-12T20:58:20+08:00">2017-06-12</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-20 21:05:32" itemprop="dateModified" datetime="2020-07-20T21:05:32+08:00">2020-07-20</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <blockquote>
<p>本文摘抄自 <strong>阮一峰</strong> 老师博客，仅供学习参考。<br>原文地址：<a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2014/10/event-loop.html</a></p>
</blockquote>
<h1 id="一、为什么JavaScript是单线程？"><a href="#一、为什么JavaScript是单线程？" class="headerlink" title="一、为什么JavaScript是单线程？"></a>一、为什么JavaScript是单线程？</h1><p>JavaScript 语言的一大特点就是单线程，也就是说，同一个时间只能做一件事。那么，为什么 JavaScript 不能有多个线程呢？这样能提高效率啊。</p>
<p>JavaScript 的单线程，与它的用途有关。作为浏览器脚本语言，JavaScript 的主要用途是与用户互动，以及操作 DOM。这决定了它只能是单线程，否则会带来很复杂的同步问题。比如，假定 JavaScript 同时有两个线程，一个线程在某个 DOM 节点上添加内容，另一个线程删除了这个节点，这时浏览器应该以哪个线程为准？</p>
<p>所以，为了避免复杂性，从一诞生，JavaScript 就是单线程，这已经成了这门语言的核心特征，将来也不会改变。</p>
<p>为了利用多核 CPU 的计算能力，HTML5 提出 Web Worker 标准，允许 JavaScript 脚本创建多个线程，但是子线程完全受主线程控制，且不得操作 DOM。所以，这个新标准并没有改变 JavaScript 单线程的本质。</p>
<h1 id="二、任务队列"><a href="#二、任务队列" class="headerlink" title="二、任务队列"></a>二、任务队列</h1><p>单线程就意味着，所有任务需要排队，前一个任务结束，才会执行后一个任务。如果前一个任务耗时很长，后一个任务就不得不一直等着。</p>
<p>如果排队是因为计算量大，CPU 忙不过来，倒也算了，但是很多时候 CPU 是闲着的，因为 IO 设备（输入输出设备）很慢（比如 Ajax 操作从网络读取数据），不得不等着结果出来，再往下执行。</p>
<p>JavaScript 语言的设计者意识到，这时主线程完全可以不管 IO 设备，挂起处于等待中的任务，先运行排在后面的任务。等到 IO 设备返回了结果，再回过头，把挂起的任务继续执行下去。</p>
<p>于是，==所有任务可以分成两种，一种是同步任务（synchronous），另一种是异步任务（asynchronous）。== 同步任务指的是，在主线程上排队执行的任务，只有前一个任务执行完毕，才能执行后一个任务；异步任务指的是，不进入主线程、而进入”任务队列”（task queue）的任务，只有”任务队列”通知主线程，某个异步任务可以执行了，该任务才会进入主线程执行。</p>
<p>具体来说，异步执行的运行机制如下。（同步执行也是如此，因为它可以被视为没有异步任务的异步执行。）</p>
<ol>
<li>所有同步任务都在主线程上执行，形成一个执行栈（execution context stack）。</li>
<li>主线程之外，还存在一个”任务队列”（task queue）。只要异步任务有了运行结果，就在”任务队列”之中放置一个事件。</li>
<li>一旦”执行栈”中的所有同步任务执行完毕，系统就会读取”任务队列”，看看里面有哪些事件。那些对应的异步任务，于是结束等待状态，进入执行栈，开始执行。</li>
<li>主线程不断重复上面的第三步。</li>
</ol>
<p>下图就是主线程和任务队列的示意图。</p>
<p><img src="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE21c14910a081cf794567059dd18e247f/5696" alt="主线程和任务队列示意图"></p>
<p>只要主线程空了，就会去读取”任务队列”，这就是 JavaScript 的运行机制。这个过程会不断重复。</p>
<h1 id="三、事件和回调函数"><a href="#三、事件和回调函数" class="headerlink" title="三、事件和回调函数"></a>三、事件和回调函数</h1><p>“任务队列”是一个事件的队列（也可以理解成消息的队列），IO 设备完成一项任务，就在”任务队列”中添加一个事件，表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”，就是读取里面有哪些事件。</p>
<p>“任务队列”中的事件，除了 IO 设备的事件以外，还包括一些用户产生的事件（比如鼠标点击、页面滚动等等）。只要指定过回调函数，这些事件发生时就会进入”任务队列”，等待主线程读取。</p>
<p>所谓”回调函数”（callback），就是那些会被主线程挂起来的代码。异步任务必须指定回调函数，当主线程开始执行异步任务，就是执行对应的回调函数。</p>
<p>“任务队列”是一个先进先出的数据结构，排在前面的事件，优先被主线程读取。主线程的读取过程基本上是自动的，只要执行栈一清空，”任务队列”上第一位的事件就自动进入主线程。但是，由于存在后文提到的”定时器”功能，主线程首先要检查一下执行时间，某些事件只有到了规定的时间，才能返回主线程。</p>
<h1 id="四、Event-Loop"><a href="#四、Event-Loop" class="headerlink" title="四、Event Loop"></a>四、Event Loop</h1><p>主线程从”任务队列”中读取事件，这个过程是循环不断的，所以整个的这种运行机制又称为 Event Loop（事件循环）。</p>
<p>为了更好地理解 Event Loop，请看下图（转引自Philip Roberts的演讲《Help, I’m stuck in an event-loop》）。</p>
<p><img src="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE9b30fd1cb0660cbab875c88b3bd2d287/5698" alt="event-loop 示意图"></p>
<p>上图中，主线程运行的时候，产生堆（heap）和栈（stack），栈中的代码调用各种外部 API，它们在”任务队列”中加入各种事件（click，load，done）。只要栈中的代码执行完毕，主线程就会去读取”任务队列”，依次执行那些事件所对应的回调函数。</p>
<p>执行栈中的代码（同步任务），总是在读取”任务队列”（异步任务）之前执行。请看下面这个例子。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var req &#x3D; new XMLHttpRequest();</span><br><span class="line">    req.open(&#39;GET&#39;, url);    </span><br><span class="line">    req.onload &#x3D; function ()&#123;&#125;;    </span><br><span class="line">    req.onerror &#x3D; function ()&#123;&#125;;    </span><br><span class="line">    req.send();</span><br></pre></td></tr></table></figure>

<p>上面代码中的 <code>req.send</code> 方法是 Ajax 操作向服务器发送数据，它是一个异步任务，意味着只有当前脚本的所有代码执行完，系统才会去读取”任务队列”。所以，它与下面的写法等价。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var req &#x3D; new XMLHttpRequest();</span><br><span class="line">req.open(&#39;GET&#39;, url);</span><br><span class="line">req.send();</span><br><span class="line">req.onload &#x3D; function ()&#123;&#125;;    </span><br><span class="line">req.onerror &#x3D; function ()&#123;&#125;;</span><br></pre></td></tr></table></figure>

<p>也就是说，指定回调函数的部分（onload 和 onerror），在 <code>send()</code> 方法的前面或后面无关紧要，因为它们属于执行栈的一部分，系统总是执行完它们，才会去读取”任务队列”。</p>
<h1 id="五、定时器"><a href="#五、定时器" class="headerlink" title="五、定时器"></a>五、定时器</h1><p>除了放置异步任务的事件，”任务队列”还可以放置定时事件，即指定某些代码在多少时间之后执行。这叫做”定时器”（timer）功能，也就是定时执行的代码。</p>
<p>定时器功能主要由 <code>setTimeout()</code> 和 <code>setInterval()</code> 这两个函数来完成，它们的内部运行机制完全一样，区别在于前者指定的代码是一次性执行，后者则为反复执行。以下主要讨论 <code>setTimeout()</code>。</p>
<p><code>setTimeout()</code> 接受两个参数，第一个是回调函数，第二个是推迟执行的毫秒数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">console.log(1);</span><br><span class="line">setTimeout(function()&#123;console.log(2);&#125;,1000);</span><br><span class="line">console.log(3);</span><br></pre></td></tr></table></figure>

<p>上面代码的执行结果是 <code>1，3，2</code>，因为 <code>setTimeout()</code> 将第二行推迟到 <code>1000</code> 毫秒之后执行。</p>
<p>如果将 <code>setTimeout()</code> 的第二个参数设为 <code>0</code>，就表示当前代码执行完（执行栈清空）以后，立即执行（<code>0</code> 毫秒间隔）指定的回调函数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">setTimeout(function()&#123;console.log(1);&#125;, 0);</span><br><span class="line">console.log(2);</span><br></pre></td></tr></table></figure>

<p>上面代码的执行结果总是 <code>2，1</code>，因为只有在执行完第二行以后，系统才会去执行”任务队列”中的回调函数。</p>
<p>总之，<code>setTimeout(fn,0)</code> 的含义是，指定某个任务在主线程最早可得的空闲时间执行，也就是说，尽可能早得执行。它在”任务队列”的尾部添加一个事件，因此要等到同步任务和”任务队列”现有的事件都处理完，才会得到执行。</p>
<p>HTML5 标准规定了 <code>setTimeout()</code> 的第二个参数的最小值（最短间隔），不得低于 <code>4</code> 毫秒，如果低于这个值，就会自动增加。在此之前，老版本的浏览器都将最短间隔设为 <code>10</code> 毫秒。另外，对于那些 DOM 的变动（尤其是涉及页面重新渲染的部分），通常不会立即执行，而是每 <code>16</code> 毫秒执行一次。这时使用 <code>requestAnimationFrame()</code> 的效果要好于 <code>setTimeout()</code>。</p>
<p>需要注意的是，<code>setTimeout()</code> 只是将事件插入了”任务队列”，必须等到当前代码（执行栈）执行完，主线程才会去执行它指定的回调函数。要是当前代码耗时很长，有可能要等很久，所以并没有办法保证，回调函数一定会在 <code>setTimeout()</code> 指定的时间执行。</p>
<h1 id="六、Node-js的Event-Loop"><a href="#六、Node-js的Event-Loop" class="headerlink" title="六、Node.js的Event Loop"></a>六、Node.js的Event Loop</h1><p>Node.js 也是单线程的 Event Loop，但是它的运行机制不同于浏览器环境。</p>
<p>请看下面的示意图（作者@BusyRich）。</p>
<p><img src="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE3a8233aa379e3bc6a52b742963e70b0f/5700" alt="node.js 事件循环示意图"></p>
<p>根据上图，Node.js的运行机制如下。</p>
<ol>
<li>V8 引擎解析 JavaScript 脚本。</li>
<li>解析后的代码，调用 Node API。</li>
<li>libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程，形成一个 Event Loop（事件循环），以异步的方式将任务的执行结果返回给V8引擎。</li>
<li>V8 引擎再将结果返回给用户。</li>
</ol>
<p>除了 <code>setTimeout</code> 和 <code>setInterval</code> 这两个方法，Node.js 还提供了另外两个与”任务队列”有关的方法：<code>process.nextTick</code> 和 <code>setImmediate</code>。它们可以帮助我们加深对”任务队列”的理解。</p>
<p><code>process.nextTick</code> 方法可以在当前”执行栈”的尾部—-下一次 Event Loop（主线程读取”任务队列”）之前—-触发回调函数。也就是说，它指定的任务总是发生在所有异步任务之前。<code>setImmediate</code> 方法则是在当前”任务队列”的尾部添加事件，也就是说，它指定的任务总是在下一次 Event Loop 时执行，这与 <code>setTimeout(fn, 0)</code> 很像。请看下面的例子（via StackOverflow）。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">process.nextTick(function A() &#123;</span><br><span class="line">  console.log(1);</span><br><span class="line">  process.nextTick(function B()&#123;console.log(2);&#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">setTimeout(function timeout() &#123;</span><br><span class="line">  console.log(&#39;TIMEOUT FIRED&#39;);</span><br><span class="line">&#125;, 0)</span><br><span class="line">&#x2F;&#x2F; 1</span><br><span class="line">&#x2F;&#x2F; 2</span><br><span class="line">&#x2F;&#x2F; TIMEOUT FIRED</span><br></pre></td></tr></table></figure>

<p>上面代码中，由于 <code>process.nextTick</code> 方法指定的回调函数，总是在当前”执行栈”的尾部触发，所以不仅函数 <code>A</code> 比 <code>setTimeout</code> 指定的回调函数 <code>timeout</code> 先执行，而且函数 <code>B</code> 也比 <code>timeout</code> 先执行。这说明，如果有多个 <code>process.nextTick</code> 语句（不管它们是否嵌套），将全部在当前”执行栈”执行。</p>
<p>现在，再看 <code>setImmediate</code>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">setImmediate(function A() &#123;</span><br><span class="line">  console.log(1);</span><br><span class="line">  setImmediate(function B()&#123;console.log(2);&#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">setTimeout(function timeout() &#123;</span><br><span class="line">  console.log(&#39;TIMEOUT FIRED&#39;);</span><br><span class="line">&#125;, 0);</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>setImmediate</code> 与 <code>setTimeout(fn,0)</code> 各自添加了一个回调函数 <code>A</code> 和 <code>timeout</code>，都是在下一次 Event Loop 触发。那么，哪个回调函数先执行呢？答案是不确定。运行结果可能是 <code>1--TIMEOUT FIRED--2</code>，也可能是 <code>TIMEOUT FIRED--1--2</code>。</p>
<p>令人困惑的是，<code>Node.js</code> 文档中称，<code>setImmediate</code> 指定的回调函数，总是排在 <code>setTimeout</code> 前面。实际上，这种情况只发生在递归调用的时候。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">setImmediate(function ()&#123;</span><br><span class="line">  setImmediate(function A() &#123;</span><br><span class="line">    console.log(1);</span><br><span class="line">    setImmediate(function B()&#123;console.log(2);&#125;);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  setTimeout(function timeout() &#123;</span><br><span class="line">    console.log(&#39;TIMEOUT FIRED&#39;);</span><br><span class="line">  &#125;, 0);</span><br><span class="line">&#125;);</span><br><span class="line">&#x2F;&#x2F; 1</span><br><span class="line">&#x2F;&#x2F; TIMEOUT FIRED</span><br><span class="line">&#x2F;&#x2F; 2</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>setImmediate</code> 和 <code>setTimeout</code> 被封装在一个 <code>setImmediate</code> 里面，它的运行结果总是 <code>1--TIMEOUT FIRED--2</code>，这时函数 <code>A</code> 一定在 <code>timeout</code> 前面触发。至于 <code>2</code> 排在 <code>TIMEOUT FIRED</code> 的后面（即函数 <code>B</code> 在 <code>timeout</code> 后面触发），是因为 <code>setImmediate</code> 总是将事件注册到下一轮 Event Loop，所以函数 <code>A</code> 和 <code>timeout</code> 是在同一轮 Loop 执行，而函数 <code>B</code> 在下一轮 Loop 执行。</p>
<p>我们由此得到了 <code>process.nextTick</code> 和 <code>setImmediate</code> 的一个重要区别：多个 <code>process.nextTick</code> 语句总是在当前”执行栈”一次执行完，多个 <code>setImmediate</code> 可能则需要多次 loop 才能执行完。事实上，这正是 Node.js 10.0 版添加 <code>setImmediate</code> 方法的原因，否则像下面这样的递归调用 <code>process.nextTick</code>，将会没完没了，主线程根本不会去读取”事件队列”！</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">process.nextTick(function foo() &#123;</span><br><span class="line">  process.nextTick(foo);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>事实上，现在要是你写出递归的 <code>process.nextTick</code>，Node.js 会抛出一个警告，要求你改成 <code>setImmediate</code>。</p>
<p>另外，由于 <code>process.nextTick</code> 指定的回调函数是在本次”事件循环”触发，而 <code>setImmediate</code> 指定的是在下次”事件循环”触发，所以很显然，前者总是比后者发生得早，而且执行效率也高（因为不用检查”任务队列”）。</p>

    </div>

    
    
    

      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2017/05/21/%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BB%A7%E6%89%BF/" rel="prev" title="对象的继承">
      <i class="fa fa-chevron-left"></i> 对象的继承
    </a></div>
      <div class="post-nav-item">
    <a href="/2017/12/15/%E8%80%81%E7%94%9F%E5%B8%B8%E8%B0%88%E2%80%94%E2%80%94call-apply-bind/" rel="next" title="老生常谈——call,apply,bind">
      老生常谈——call,apply,bind <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#一、为什么JavaScript是单线程？"><span class="nav-number">1.</span> <span class="nav-text">一、为什么JavaScript是单线程？</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#二、任务队列"><span class="nav-number">2.</span> <span class="nav-text">二、任务队列</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#三、事件和回调函数"><span class="nav-number">3.</span> <span class="nav-text">三、事件和回调函数</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#四、Event-Loop"><span class="nav-number">4.</span> <span class="nav-text">四、Event Loop</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#五、定时器"><span class="nav-number">5.</span> <span class="nav-text">五、定时器</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#六、Node-js的Event-Loop"><span class="nav-number">6.</span> <span class="nav-text">六、Node.js的Event Loop</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Albert Guo"
      src="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
  <p class="site-author-name" itemprop="name">Albert Guo</p>
  <div class="site-description" itemprop="description">郭鹏松的个人博客</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives">
          <span class="site-state-item-count">19</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">6</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="mailto:albert-guo@outlook.com" title="E-Mail → mailto:albert-guo@outlook.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Albert Guo</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
